package com.azarias.dfs.namenode.server.component;


import com.azarias.dfs.namenode.server.component.EditLog;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;

/**
 * @description: some desc
 * @author: Azarias_Jx
 * @date: 2021/12/31 17:51
 */

/**
 * 双buffer缓存机制
 */
public class DoubleBuffer {
    /**
     * 单块editslog缓冲区的最大大小：默认是512字节
     */
    public static final Integer EDIT_LOG_BUFFER_LIMIT = 20 * 1024;

    /**
     * 是专门用来承载线程写入edits log
     */
    private EditLogBuffer currentBuffer = new EditLogBuffer();
    /**
     * 专门用来将数据同步到磁盘中去的一块缓冲
     */
    private EditLogBuffer syncBuffer = new EditLogBuffer();

    /**
     * 当前这块缓冲区写入的最大的一个txid
     */
    long startTxid = 1L;
    /**
     * 写入到写buffer
     *
     * @param editLog
     */
    public void write(EditLog editLog) throws IOException {
        currentBuffer.write(editLog);
    }

    /**
     * 交换buffer,让syncBuffer去写入磁盘
     */
    public void setCurrentToSync() {
        EditLogBuffer tmp = currentBuffer;
        currentBuffer = syncBuffer;
        syncBuffer = tmp;
    }


    /**
     * 将syncBuffer写入磁盘
     */
    public void flush() throws IOException {
        syncBuffer.flush();
        syncBuffer.clear();
    }

    public boolean shouldSyncToDisk() {
        if (currentBuffer.size() >= EDIT_LOG_BUFFER_LIMIT) {
            return true;
        }
        return false;
    }

    /**
     * editslog缓冲区
     */
    class EditLogBuffer {
        private ByteArrayOutputStream buffer = new ByteArrayOutputStream(EDIT_LOG_BUFFER_LIMIT);
        /**
         * 上一次flush到磁盘的时候他的最大的txid是多少
         */
        long endTxid = 0L;
        /**
         * 将editslog日志写入缓冲区
         * 按照换行符分隔数据
         *
         * @param log
         */
        public void write(EditLog log) throws IOException {
            endTxid = log.getTxid();
            buffer.write(log.getContent().getBytes(StandardCharsets.UTF_8));
            buffer.write("\n".getBytes());
            System.out.println("写入一条editslog：" + log.getContent()
                    + "，当前缓冲区的大小是：" + size());
        }

        /**
         * 获取当前缓冲区已经写入数据的字节数量
         *
         * @return
         */
        public Integer size() {
            return buffer.size();
        }

        /**
         * 将syncbuffer数据刷入磁盘
         *
         * @throws IOException
         */
        public void flush() throws IOException {
            byte[] bytes = buffer.toByteArray();
            ByteBuffer wrap = ByteBuffer.wrap(bytes);

            FileChannel channel = null;
            FileOutputStream outputStream = null;
            RandomAccessFile file = null;

            String editsLogFilePath = "E:\\code_gen\\editslog\\edits-"
                    + startTxid + "-" + endTxid + ".log";
            try {
                file = new RandomAccessFile(editsLogFilePath, "rw");
                outputStream = new FileOutputStream(file.getFD());
                channel = outputStream.getChannel();
                channel.write(wrap);
                channel.force(false);//如果不这样写的话，现在是写在内存里的，这边直接强制刷盘
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if (null != channel) {
                    channel.close();
                }
                if (null != outputStream) {
                    outputStream.close();
                }
                if (null != file) {
                    file.close();
                }

            }
            startTxid = endTxid + 1;
        }

        public void clear() {
            buffer.reset();
        }

    }
}